package collector

import (
	"time"

	"gitee.com/openeuler/uos-libvirtd-exporter/slogx"
	"github.com/prometheus/client_golang/prometheus"
	"libvirt.org/go/libvirt"
)

// DiskCollector collects disk I/O statistics
type DiskCollector struct {
	vmDiskReadBytes  *prometheus.Desc
	vmDiskWriteBytes *prometheus.Desc
	vmDiskReadOps    *prometheus.Desc
	vmDiskWriteOps   *prometheus.Desc
	vmDiskReadTime   *prometheus.Desc
	vmDiskWriteTime  *prometheus.Desc
	metricsCollector MetricsCollector
}

// NewDiskCollector creates a new DiskCollector
func NewDiskCollector() *DiskCollector {
	return &DiskCollector{
		vmDiskReadBytes: prometheus.NewDesc(
			"libvirt_vm_disk_read_bytes_total",
			"Total bytes read from disk by the virtual machine",
			[]string{"domain", "uuid", "device"},
			nil,
		),
		vmDiskWriteBytes: prometheus.NewDesc(
			"libvirt_vm_disk_write_bytes_total",
			"Total bytes written to disk by the virtual machine",
			[]string{"domain", "uuid", "device"},
			nil,
		),
		vmDiskReadOps: prometheus.NewDesc(
			"libvirt_vm_disk_read_ops_total",
			"Total disk read operations by the virtual machine",
			[]string{"domain", "uuid", "device"},
			nil,
		),
		vmDiskWriteOps: prometheus.NewDesc(
			"libvirt_vm_disk_write_ops_total",
			"Total disk write operations by the virtual machine",
			[]string{"domain", "uuid", "device"},
			nil,
		),
		vmDiskReadTime: prometheus.NewDesc(
			"libvirt_vm_disk_read_time_seconds_total",
			"Total time spent reading from disk by the virtual machine",
			[]string{"domain", "uuid", "device"},
			nil,
		),
		vmDiskWriteTime: prometheus.NewDesc(
			"libvirt_vm_disk_write_time_seconds_total",
			"Total time spent writing to disk by the virtual machine",
			[]string{"domain", "uuid", "device"},
			nil,
		),
		metricsCollector: NewLibvirtMetricsCollector(),
	}
}

// Describe implements the prometheus.Collector interface for DiskCollector
func (c *DiskCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- c.vmDiskReadBytes
	ch <- c.vmDiskWriteBytes
	ch <- c.vmDiskReadOps
	ch <- c.vmDiskWriteOps
	ch <- c.vmDiskReadTime
	ch <- c.vmDiskWriteTime
}

// Collect implements the Collector interface for DiskCollector
func (c *DiskCollector) Collect(
	ch chan<- prometheus.Metric,
	conn *libvirt.Connect,
	domain *libvirt.Domain,
) {
	startTime := time.Now()
	domainName, _ := domain.GetName()

	// Get domain info first to check if it's running
	domainInfo, err := domain.GetInfo()
	if err != nil {
		slogx.Warn("Failed to get domain info for disk metrics",
			"domain", domainName,
			"error", err,
			"collector", "disk")
		return
	}

	// Only collect disk metrics for running domains
	if domainInfo.State != libvirt.DOMAIN_RUNNING {
		// Silently skip non-running domains - this is expected behavior
		slogx.Debug("Skipping non-running domain for disk metrics",
			"domain", domainName,
			"state", domainInfo.State)
		return
	}

	// Get block device stats
	blockStats, err := c.metricsCollector.CollectDiskStats(conn, domain)
	if err != nil {
		slogx.Warn("Failed to collect block device stats",
			"domain", domainName,
			"error", err,
			"collector", "disk",
			"duration_ms", time.Since(startTime).Milliseconds())
		return
	}

	// Get domain UUID
	domainUUID, err := domain.GetUUIDString()
	if err != nil {
		slogx.Warn("Failed to get domain UUID",
			"domain", domainName,
			"error", err,
			"collector", "disk")
		return
	}

	// Send metrics to channel
	for _, stat := range blockStats {
		ch <- prometheus.MustNewConstMetric(
			c.vmDiskReadBytes,
			prometheus.CounterValue,
			float64(stat.ReadBytes),
			domainName, domainUUID, stat.Device,
		)

		ch <- prometheus.MustNewConstMetric(
			c.vmDiskWriteBytes,
			prometheus.CounterValue,
			float64(stat.WriteBytes),
			domainName, domainUUID, stat.Device,
		)

		ch <- prometheus.MustNewConstMetric(
			c.vmDiskReadOps,
			prometheus.CounterValue,
			float64(stat.ReadOps),
			domainName, domainUUID, stat.Device,
		)

		ch <- prometheus.MustNewConstMetric(
			c.vmDiskWriteOps,
			prometheus.CounterValue,
			float64(stat.WriteOps),
			domainName, domainUUID, stat.Device,
		)

		ch <- prometheus.MustNewConstMetric(
			c.vmDiskReadTime,
			prometheus.CounterValue,
			float64(stat.ReadTimeNs)/1e9, // Convert nanoseconds to seconds
			domainName, domainUUID, stat.Device,
		)

		ch <- prometheus.MustNewConstMetric(
			c.vmDiskWriteTime,
			prometheus.CounterValue,
			float64(stat.WriteTimeNs)/1e9, // Convert nanoseconds to seconds
			domainName, domainUUID, stat.Device,
		)
	}

	slogx.Debug("Disk metrics collected successfully",
		"domain", domainName,
		"devices", len(blockStats),
		"duration_ms", time.Since(startTime).Milliseconds())
}

// Reset implements the Collector interface for DiskCollector
func (c *DiskCollector) Reset() {
	// No internal state to reset
}
